// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "stdafx.h"
#include <windows.h>
#include "eventsink.h"
#include "Network.h"

//
// FUNCTION: StartListeningForEvents
//
// Creates the CEventSink object, and start a thread to perform an advise on
// it.
//
HRESULT CEventSink::StartListeningForEvents(REFIID riid, CEventSink** ppEventResponse, HWND hWndNotify)
{
	if (!ppEventResponse)
	{
		return E_POINTER;
	}

	HRESULT hr = S_OK;

	// Create our CEventSink object that will be used to advise to the Connection
	// point
	CEventSink *pEventResponse = new CComObject<CEventSink>();
	if (pEventResponse)
	{
		pEventResponse->AddRef();
		pEventResponse->m_riid = riid;

		// If you also want to use CEventSink in this thread, you should 
		// use CoMarshalInterThreadInterfaceInStream and pass the IStream* 
		// instead of the CEventSink* here.
		//
		// As it is, the CEventSink is only initialized and used on
		// the thread being created (inside ListenForEvents), 
		// and we can avoid the cross-apartment marshalling.
		pEventResponse->m_hThread = CreateThread(NULL, 
			0, 
			&StartListeningForEventsThread, 
			pEventResponse, 
			0, 
			&(pEventResponse->m_dwThreadId));

		if (pEventResponse->m_hThread == INVALID_HANDLE_VALUE)
		{
			DWORD dwError = GetLastError();
			hr = HRESULT_FROM_WIN32(dwError);
		}

		if (SUCCEEDED(hr))
		{
			*ppEventResponse = pEventResponse;
			(*ppEventResponse)->AddRef();
			(*ppEventResponse)->m_hWndNotify = hWndNotify;
		}

		pEventResponse->Release();
	}
	else
	{
		hr = E_OUTOFMEMORY;
	}

	return hr;
}

//
// FUNCTION: StartListeningForEventsThread
//
// This is our thread entry proc for the thread that will listen on events.
//
DWORD WINAPI CEventSink::StartListeningForEventsThread(LPVOID pArg)
{
	HRESULT hr = S_OK;

	HRESULT hrCoinit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	if (SUCCEEDED(hrCoinit) || (RPC_E_CHANGED_MODE == hrCoinit))
	{
		CEventSink* pThis = reinterpret_cast<CEventSink*>(pArg);
		hr = pThis->ListenForEvents();

		if (RPC_E_CHANGED_MODE != hrCoinit)
		{
			CoUninitialize();
		}
	}
	else
	{
		hr = hrCoinit;
	}

	return hr;
}

//
// FUNCTION: ListenForEvents
//
// The main listener function. Listens, and waits on a Message Loop. 
// Stop this thread by posting WM_QUIT to it.
//
HRESULT CEventSink::ListenForEvents()
{
	HRESULT hr = S_OK;

	CComPtr<INetworkListManager> pNLM;
	hr = CoCreateInstance(CLSID_NetworkListManager, NULL, 
		CLSCTX_ALL, __uuidof(INetworkListManager), (LPVOID*)&pNLM);
	if (SUCCEEDED(hr))
	{
		CComPtr<IConnectionPointContainer> pCpc;
		hr = pNLM->QueryInterface(IID_IConnectionPointContainer, (void**)&pCpc);
		if (SUCCEEDED(hr))
		{
			hr = pCpc->FindConnectionPoint(m_riid, &m_pConnectionPoint);
			if (SUCCEEDED(hr))
			{
				CComPtr<IUnknown> pSink;
				hr = this->QueryInterface(IID_IUnknown, (void**)&pSink);
				if (SUCCEEDED(hr))
				{
					hr = m_pConnectionPoint->Advise(pSink, &m_dwCookie);
					if (SUCCEEDED(hr))
					{
						wprintf(L"Got back %d cookie for riid %s\r\n", m_dwCookie, GetSzGuid(m_riid));
						BOOL bRet;
						MSG msg;
						while((bRet = GetMessage(&msg, NULL, 0, 0 )) != 0)
						{ 
							if (bRet == -1)
							{
								break;
							}

							TranslateMessage(&msg); 
							DispatchMessage(&msg); 
						}
					}
				}
			}
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
	}

	return hr;
}

//
// FUNCTION: StopListeningForEvents
//
// Stops the event listener thread by posting WM_QUIT to it, then 
// wait for it to exit, and return the exit code.
//
HRESULT CEventSink::StopListeningForEvents()
{
	HRESULT hr = S_OK;

	if (m_pConnectionPoint != NULL)
	{
		hr = m_pConnectionPoint->Unadvise(m_dwCookie);
	}

	if (m_hThread != INVALID_HANDLE_VALUE)
	{
		PostThreadMessage(m_dwThreadId, WM_QUIT, 0, 0);
		WaitForSingleObject(m_hThread, INFINITE);

		DWORD dwExitCode;
		GetExitCodeThread(m_hThread, &dwExitCode);
		hr = dwExitCode;
		CloseHandle(m_hThread);
	}

	return hr;
}

//
// FUNCTION: ConnectivityChanged
//
// Sample implementation of INetworkListManagerEvents::ConnectivityChanged
//
STDMETHODIMP CEventSink::ConnectivityChanged(NLM_CONNECTIVITY NewConnectivity)
{
	wprintf(L"EVENT: New machine connectivity : %08x %s\n", NewConnectivity, NLM_CONNECTIVITY_ToString(NewConnectivity));
	if (m_hWndNotify)
		PostMessage(m_hWndNotify, UM_NETWORKMONITOR, NewConnectivity, 0);
	return S_OK;
}

//
// FUNCTION: NetworkAdded
//
// Sample implementation of INetworkEvents::NetworkAdded
//
STDMETHODIMP CEventSink::NetworkAdded(GUID networkId)
{
	wprintf(L"EVENT: Network added : %s\r\n", GetSzGuid(networkId));

	// Call back and show the rest of the network information
	CComPtr<INetworkListManager> pNLM;
	HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, NULL, 
		CLSCTX_ALL, __uuidof(INetworkListManager), (LPVOID*)&pNLM);
	if (SUCCEEDED(hr))
	{
		CComPtr<INetwork> pNetwork;
		hr = pNLM->GetNetwork(networkId, &pNetwork);
		if (SUCCEEDED(hr))
		{
			ShowInfoForNetwork(pNetwork);
		}
	}

	return S_OK;
}

//
// FUNCTION: NetworkDeleted
//
// Sample implementation of INetworkEvents::NetworkDeleted
//
STDMETHODIMP CEventSink::NetworkDeleted(GUID networkId)
{
	wprintf(L"EVENT: Network deleted : %s\r\n", GetSzGuid(networkId));
	return S_OK;
}

//
// FUNCTION: NetworkConnectivityChanged
//
// Sample implementation of INetworkEvents::NetworkConnectivityChanged
//
STDMETHODIMP CEventSink::NetworkConnectivityChanged(GUID networkId,  NLM_CONNECTIVITY newConnectivity)
{
	wprintf(L"EVENT: Network connectivity changed : %s to %08x (%s)\r\n", 
		GetSzGuid(networkId), 
		newConnectivity, 
		NLM_CONNECTIVITY_ToString(newConnectivity));
	return S_OK;
}

//
// FUNCTION: NetworkPropertyChanged
//
// Sample implementation of INetworkEvents::NetworkPropertyChange
//
STDMETHODIMP CEventSink::NetworkPropertyChanged(GUID networkId, NLM_NETWORK_PROPERTY_CHANGE flags)
{
	wprintf(L"EVENT: Network property changed : %s - %s\r\n", GetSzGuid(networkId), NLM_NETWORK_PROPERTY_CHANGE_ToString(flags));
	return S_OK;
}

//
// FUNCTION: NetworkConnectionConnectivityChanged
//
// Sample implementation of INetworkConnectionEvents::NetworkConnectionConnectivityChanged
//
STDMETHODIMP CEventSink::NetworkConnectionConnectivityChanged(GUID networkId,  NLM_CONNECTIVITY newConnectivity)
{
	wprintf(L"EVENT: Network connection connectivity changed : %s to %08x (%s): %s\r\n", GetSzGuid(networkId), newConnectivity, NLM_CONNECTIVITY_ToString(newConnectivity));
	return S_OK;
}

//
// FUNCTION: NetworkConnectionPropertyChanged
//
// Sample implementation of INetworkConnectionEvents::NetworkConnectionPropertyChange
//
STDMETHODIMP CEventSink::NetworkConnectionPropertyChanged(GUID networkId, NLM_CONNECTION_PROPERTY_CHANGE flags)
{
	wprintf(L"EVENT: Network connection property changed : %s - %s\r\n", GetSzGuid(networkId), NLM_CONNECTION_PROPERTY_CHANGE_ToString(flags));
	return S_OK;
}
